package graphUtility.test1;

import java.awt.Point;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.OutputStream;
import java.util.LinkedList;

import javax.xml.transform.TransformerConfigurationException;

import org.jgrapht.Graph;
import org.jgrapht.alg.DijkstraShortestPath;
import org.jgrapht.ext.GmlExporter;
import org.jgrapht.ext.GraphMLExporter;
import org.jgrapht.ext.VisioExporter;
import org.jgrapht.graph.ListenableDirectedWeightedGraph;
import org.jgrapht.graph.ListenableUndirectedGraph;
import org.jgrapht.graph.SimpleGraph;
import org.jgrapht.graph.SimpleWeightedGraph;
import org.jgrapht.traverse.BreadthFirstIterator;
import org.jgrapht.traverse.DepthFirstIterator;
import org.xml.sax.SAXException;

import vacuumCleaner.Action;
import vacuumCleaner.Action.Type;
import vacuumCleaner.Agent;
import vacuumCleaner.Environment;
import vacuumCleaner.Floor;
import vacuumCleaner.Perception;
import vacuumCleaner.Square;
import sun.misc.Queue;

public class GraphFactory {

	private LinkedList<Action.Type> mossePossibili=new LinkedList<Action.Type>();
	private Graph<State,MyEdge> simpleGraph;
	private Environment environment;
	private Perception myAgentPerception;
	private LinkedList<Point> listaCoordDirtySquares;
	Queue q=new Queue();
	
	State start;
	State finish=null;

	
	public GraphFactory(Environment environment) {
		// TODO Auto-generated constructor stub
		simpleGraph=new SimpleWeightedGraph <State,MyEdge>(MyEdge.class);
		this.environment=environment;
	}
	
	public void initBuildGraph() throws InterruptedException
	{
		listaCoordDirtySquares=getCoordinateDirtySquares();

		start=new State("Start",new Point(environment.agent.x,environment.agent.y), listaCoordDirtySquares);
		simpleGraph.addVertex(start);
		q.enqueue(start);
		generateNewGraph(q);
		
	}
	
	public Graph<State, MyEdge> generateNewGraph(Queue q) throws InterruptedException
	{
	
		
		if(finish!=null)
			return simpleGraph;

		while (!q.isEmpty() && finish==null)
		{	
			State currentRoot=(State)q.dequeue();
						
							if(canAddVertex(Type.NORTH,currentRoot))
							{
								LinkedList<Point> listaClonataNorth=(LinkedList<Point>)currentRoot.getCoordinateMattonelleSporche().clone();
								State child=new State("North",new Point(currentRoot.getAgentPosition().x-1,currentRoot.getAgentPosition().y),listaClonataNorth);
								addVertexEdge(currentRoot, child);
							}
							if(canAddVertex(Type.SOUTH,currentRoot))
							{
								LinkedList<Point> listaClonataSouth=(LinkedList<Point>)currentRoot.getCoordinateMattonelleSporche().clone();
								State child=new State("South",new Point(currentRoot.getAgentPosition().x+1,currentRoot.getAgentPosition().y),listaClonataSouth);
								addVertexEdge(currentRoot, child);
							}
							if(canAddVertex(Type.WEST,currentRoot))
							{
								LinkedList<Point> listaClonataWest=(LinkedList<Point>)currentRoot.getCoordinateMattonelleSporche().clone();
								State child=new State("West",new Point(currentRoot.getAgentPosition().x,currentRoot.getAgentPosition().y-1),listaClonataWest);
								addVertexEdge(currentRoot, child);
							}
							if(canAddVertex(Type.EAST,currentRoot))
							{
								LinkedList<Point> listaClonataEast=(LinkedList<Point>)currentRoot.getCoordinateMattonelleSporche().clone();

								State child=new State("East",new Point(currentRoot.getAgentPosition().x,currentRoot.getAgentPosition().y+1),listaClonataEast);
								addVertexEdge(currentRoot, child);
							}
							if(canAddVertex(Type.SUCK,currentRoot) && !currentRoot.getEtichetta().equals("Suck"))
							{
								//System.out.println("STO SUCCHIANDO "+currentRoot.getAgentPosition().getX()+" "+currentRoot.getAgentPosition().getY());
								LinkedList<Point> listaClonataSuck=(LinkedList<Point>)currentRoot.getCoordinateMattonelleSporche().clone();

								//listaCoordDirtySquares.remove(new Point(currentRoot.getAgentPosition().x,currentRoot.getAgentPosition().y));
								listaClonataSuck.remove(new Point(currentRoot.getAgentPosition().x,currentRoot.getAgentPosition().y));
								State childSuck=new State("Suck",new Point(currentRoot.getAgentPosition().x,currentRoot.getAgentPosition().y),listaClonataSuck);
								//if(!simpleGraph.containsVertex(child))
								
								addVertexEdge(currentRoot, childSuck);
								
									if(listaClonataSuck.isEmpty())
									{
										
										 finish=new State("Finish",new Point(-1,-1),null);
										 addVertexEdge(childSuck, finish);
										 System.out.println("Ho finito di generare il grafo! Numero nodi: "+simpleGraph.vertexSet().size());
										 
									}
									
							
							}
		} //fine while
		generateNewGraph(q);
		//System.out.println("Mattonelle Sporche "+environment.floor.dirtySquares());
		return simpleGraph;
		//return null;
	}
	
	public void addVertexEdge(State currentRoot, State child)
	{
		
		MyEdge currentEdge=new MyEdge(currentRoot, child);
		
		if(simpleGraph.addVertex(child))
		{
			simpleGraph.addEdge(currentEdge.getSource(),currentEdge.getTarget(),currentEdge);
			q.enqueue(child);
		}
		
	}
	
	public void searchPath()
	{
		DijkstraShortestPath<State, MyEdge> dij=new DijkstraShortestPath<State, MyEdge>(simpleGraph, start, finish);
		
		for( MyEdge f :dij.getPathEdgeList())
		{
			//String mossa=getStringaMossa(f);
			//System.out.println("."+mossa+".");
			System.out.println(f.getTarget().getEtichetta());
			mossePossibili.add(StringToActionType(f.getTarget().getEtichetta()));
		}
			
	}
	
	
	public Action.Type StringToActionType(String mossa)
	{
		if(mossa.equals("South"))
			return Action.Type.SOUTH;
		else if(mossa.equals("North"))
				return Action.Type.NORTH;
		else if(mossa.equals("West"))
				return Action.Type.WEST;
		else if(mossa.equals("East"))
			return Action.Type.EAST;
		else if(mossa.equals("Suck"))
			return Action.Type.SUCK;
		else
			return Action.Type.NOOP;
	}
	
	public State getStart() {
		return start;
	}

	public void setStart(State start) {
		this.start = start;
	}

	boolean canAddVertex(Type type,State currentRoot)
	{
		
	
			if(currentRoot.getAgentPosition().x>0 && type==Type.NORTH &&
				myAgentPerception.floor.get(currentRoot.getAgentPosition().x-1,currentRoot.getAgentPosition().y)!=Square.Type.OBSTACLE)
				return true;
			else if(currentRoot.getAgentPosition().x<myAgentPerception.floor.length-1 && type==Type.SOUTH &&
					myAgentPerception.floor.get(currentRoot.getAgentPosition().x+1,currentRoot.getAgentPosition().y)!=Square.Type.OBSTACLE)
				return true;
			else if(currentRoot.getAgentPosition().y>0 && type==Type.WEST &&
					myAgentPerception.floor.get(currentRoot.getAgentPosition().x,currentRoot.getAgentPosition().y-1)!=Square.Type.OBSTACLE)
				return true;
			else if(currentRoot.getAgentPosition().y<myAgentPerception.floor.width-1 && type==Type.EAST &&
					myAgentPerception.floor.get(currentRoot.getAgentPosition().x,currentRoot.getAgentPosition().y+1)!=Square.Type.OBSTACLE)
				return true;
			else if(myAgentPerception.floor.get(currentRoot.getAgentPosition().x,currentRoot.getAgentPosition().y)==Square.Type.DIRTY && type==Type.SUCK)
				return true;
			
		return false;
	
	}

	public Graph<State, MyEdge> getSimpleGraph() {
		return simpleGraph;
	}

	public void setSimpleGraph(Graph<State, MyEdge> simpleGraph) {
		this.simpleGraph = simpleGraph;
	}


	public LinkedList<Action.Type> getMossePossibili() {
		return mossePossibili;
	}


	public void setMossePossibili(LinkedList<Action.Type> mossePossibili) {
		this.mossePossibili = mossePossibili;
	}
	public Perception getMyPerception() {
		return myAgentPerception;
	}
	public void setMyPerception(Perception myAgentPerception) {
		this.myAgentPerception = myAgentPerception;
	}
	
	public LinkedList<Point> getCoordinateDirtySquares()
	{
		LinkedList<Point> tempList=new LinkedList<Point>();
		for(int i=0;i<environment.floor.length;i++)
			for(int j=0;j<environment.floor.width;j++)
				if(environment.floor.get(i, j)==vacuumCleaner.Square.Type.DIRTY)
					tempList.add(new Point(i, j));

		return tempList;
	}
	
	
	
	
}
